#include "move.hpp"

#include "qbe/src/ecs/app.hpp"

#include "qbe/src/ecs/query.hpp"

#include "qbe/src/axis/axis.hpp"
#include "qbe/src/input/input.hpp"
#include "qbe/src/select/select.hpp"
#include "qbe/src/state/state.hpp"
#include "qbe/src/undoRedo/undoRedo.hpp"
#include "qbe/src/utils/utils.hpp"

#include "qbe/src/layout/layout.hpp"

#include "../../select/select_cmd.hpp"

#include "qlib/all.h"

#include "components.hpp"
#include "events.hpp"

namespace qbe::move {

void clear_tag(ecs::Query query) {
  auto& move_param = query.get_resource<Param>();

  auto& cell = query.get_resource<qlib::Cell::s_ptr>();
  auto& reg = cell->reg;

  if (move_param.clear) {
    auto view = reg.view<MoveingTag>();

    view.each([&reg](entt::entity e) { reg.remove<MoveingTag>(e); });

    move_param.clear = false;
  }
}

void clear_events(ecs::Query query) {
  query.get_resource<ecs::Event<Event>>().clear();
}

// 添加临时对象
void add_cache_obj(entt::registry& reg, ecs::EventWriter<Event> event_writer, layout::LayoutCmd& cmd, entt::entity entity, bool first) {
  if (first) {
    Event event;
    event.code = Code::Begin;
    event_writer.send(std::move(event));
  }

  reg.emplace_or_replace<MoveingTag>(entity);
}

// 更新临时对象
void update_cache_obj(entt::registry& reg, ecs::EventWriter<Event> event_writer, layout::LayoutCmd& cmd, qlib::Vec2d translate_a,
                      qlib::Vec2d translate_b) {
  Event event;
  event.code = Code::Update;
  event.translate_a = translate_a;
  event.translate_b = translate_b;
  event_writer.send(std::move(event));
}

// 删除临时对象
void delete_cache_obj(entt::registry& reg, ecs::EventWriter<Event> event_writer, layout::LayoutCmd& cmd, ecs::Query query) {
  Event event;
  event.code = Code::End;
  event_writer.send(std::move(event));

  auto view = reg.view<MoveingTag>();
  view.each([&reg](entt::entity e) { reg.remove<MoveingTag>(e); });

  auto& sele_event_writer = query.get_event_writer<select::Event>();
  sele_event_writer.send({select::Code::SelectUpdate});
  sele_event_writer.send({select::Code::HoverUpdate});
}

// 退出移动模式时回调
void exit_callback(ecs::Query query, state::AllModeType mode) {
  // 获取移动参数
  auto& move_param = query.get_resource<Param>();

  // 判断移动模式是否在执行
  auto& layout_cmd = query.get_resource<layout::LayoutCmd>();
  auto event_writer = query.get_event_writer<Event>();

  // 获取reg
  auto& cell = query.get_resource<qlib::Cell::s_ptr>();
  auto& reg = cell->reg;

  // 原对象不变，仅移除标记
  auto view_old = reg.view<MoveingTag>();
  view_old.each([&reg](entt::entity entity) { reg.remove<MoveingTag>(entity); });

  // 删除临时对象
  delete_cache_obj(reg, event_writer, layout_cmd, query);

  move_param.reset();

  auto& absorbModule = query.get_resource<qbe::adsorb::AdsorbModel>();
  absorbModule.close_diagonal_start();
}

void init(ecs::Commands commands, ecs::Query query) {
  Param param;
  commands.insert_resource(param);

  auto& state = query.get_resource<state::State>();
  state.addExitCall(state::EditingModeType::Move, exit_callback);
}

// 退出模式
void exit(ecs::Query query) {
  // 获取移动参数
  auto& move_param = query.get_resource<Param>();

  auto& layout_cmd = query.get_resource<layout::LayoutCmd>();
  auto event_writer = query.get_event_writer<Event>();

  // 获取reg
  auto& cell = query.get_resource<qlib::Cell::s_ptr>();
  auto& reg = cell->reg;

  // 原对象不变，仅移除标记
  auto view_old = reg.view<MoveingTag>();
  view_old.each([&reg](entt::entity entity) { reg.remove<MoveingTag>(entity); });

  // 删除临时对象
  delete_cache_obj(reg, event_writer, layout_cmd, query);

  move_param.reset();

  auto& absorbModule = query.get_resource<qbe::adsorb::AdsorbModel>();
  absorbModule.close_diagonal_start();

  // 退出模式
  auto& state = query.get_resource<state::State>();
  state.exit();
}

// 帧开始，模式转换
void stateTransformBegin(Param& param, state::State& state, input::HandleBundle* handle, ecs::Bus& bus, ecs::Query query) {
  // assert(param.state == State::None);

  // 快捷键
  input::KeyBoardShortcuts kbs;
  kbs.keyBase = vsg::KEY_q;
  kbs.keyModifier = vsg::MODKEY_Shift;

  // 如果不处于等待模式，则按下必然弹窗
  if (param.state != State::Wait && handle->keyReleased(kbs)) {
    // 按下则发送事件
    bus.send(qbe::UiMessage::OpenMoveUi());

    // 等待ui消息
    param.state = State::Wait;
    return;
  }

  if (param.state == State::Wait) {
    // 尝试接收消息
    auto ok_mes = bus.receive<qbe::UiMessage::MoveOk>();
    auto cancel_mes = bus.receive<qbe::UiMessage::MoveCancel>();

    if (!ok_mes.empty()) {
      param.move_to_pos = ok_mes.back().moveTo;
      param.move_vec = qlib::Vec2d(ok_mes.back().x, ok_mes.back().y);

      param.state = State::Complete;
      param.use_param = true;

      return;
    }

    if (!cancel_mes.empty()) {
      if (param.moveing) {
        param.state = State::Upatde;
      } else {
        param.state = State::None;
      }
      return;
    }
  }

  if (param.state != State::Wait && handle->keyReleased(vsg::KEY_Escape)) {
    param.state = State::Cancel;
    return;
  }

  if (param.state != State::Wait && handle->mouseReleased(1) && param.moveing && !param.wait_clock) {
    param.state = State::Complete;
    param.moveing = false;
    return;
  }

  if (param.state != State::Wait && handle->mousePressed(vsg::BUTTON_MASK_1)) {
    if (param.wait_clock) {
      if (!param.first_clock) {
        param.first_clock = true;

        auto& axisModule = query.get_resource<axis::Axis>();
        param.pos_clock = {axisModule.real_x, axisModule.real_y};

        param.moveing_first = true;
        param.state = State::Upatde;
      } else {
        param.state = State::Complete;
        param.moveing = false;
      }
    } else {
      param.moveing_first = true;
      param.state = State::Upatde;
    }

    return;
  }

  if (param.state != State::Wait && param.tuodong_first) {
    param.moveing_first = true;
    param.tuodong_first = false;
    param.state = State::Upatde;
  }

  if (param.state != State::Wait && handle->mouse_move && param.moveing) {
    param.state = State::Upatde;
    return;
  }
}

// 帧尾-退出模式
void stateTransformEnd(State& state) {
  // 操作模式执行完就回退
  if (state == State::Upatde || state == State::Complete || state == State::Cancel) {
    state = State::None;
  }
}

// 获取鼠标当前点，需要考虑：吸附、斜角吸附
qlib::Vec2d getNowPoint(ecs::Query query, qlib::Vec2d prePoint) {
  {
    auto& axisModule = query.get_resource<axis::Axis>();
    qlib::Vec2d pos = {axisModule.real_x, axisModule.real_y};

    auto& absorbModule = query.get_resource<qbe::adsorb::AdsorbModel>();
    absorbModule.set_diagonal_start(prePoint);

    return pos;
  }

  auto& state = query.get_resource<state::State>();

  if (!state.isAdsorption()) {
    if (!state.isAngleTheAdsorption()) {
      auto& axisModule = query.get_resource<axis::Axis>();
      qlib::Vec2d pos = {axisModule.real_x, axisModule.real_y};
      return pos;
    } else {
      auto& axisModule = query.get_resource<axis::Axis>();
      qlib::Vec2d pos = {axisModule.real_x, axisModule.real_y};
      return utils::getAngleAdsorbedPoint(prePoint, pos);
    }
  } else {
    if (!state.isAngleTheAdsorption()) {
      return utils::getAdsorbedPoint(query);
    } else {
      qlib::Vec2d pos = utils::getAdsorbedPoint(query);
      return utils::getAngleAdsorbedPoint(prePoint, pos);
    }
  }
}

qlib::Vec2dArray boxOver(qlib::Vec2dArray box, double factor, double min_size) {
  qlib::Vec2d center = (box[0] + box[1]) / 2.0;
  double width = std::abs(std::max(box[1].x - box[0].x, min_size));
  double height = std::abs(std::max(box[1].y - box[0].y, min_size));

  width *= factor;
  height *= factor;

  return {(center - qlib::Vec2d(width / 2.0, height / 2.0)), (center + qlib::Vec2d(width / 2.0, height / 2.0))};
}

bool haveSelectPartObj(ecs::Query query) {
  auto& axisModule = query.get_resource<axis::Axis>();
  auto& select_cmd = query.get_resource<select::SelectCmd>();
  std::unordered_map<entt::entity, select::SelectPartData> partMap;

  select_cmd.query_nodes(axisModule.real_x, axisModule.real_y);
  auto value = select_cmd.get_part(axisModule.real_x, axisModule.real_y, partMap, true, true, true);
  return value;
}

// 移动逻辑
void update(ecs::Query query, ecs::Bus& bus) {
  // 判断编辑模式为移动
  auto& state = query.get_resource<state::State>();
  auto& handModule = query.get_resource<vsg::ref_ptr<input::HandleBundle>>();
  auto& move_param = query.get_resource<Param>();
  auto& axisModule = query.get_resource<axis::Axis>();

  // std::cout << "Axis pos : " << axisModule.real_x << " " << axisModule.real_y << std::endl;

  auto& cell = query.get_resource<qlib::Cell::s_ptr>();
  auto& reg = cell->reg;

  if (handModule->mouseReleased(1) && move_param.enter_to_move) {
    move_param.enter_to_move = false;
    // std::cout << "No Enabel Enter Move ---------------" << std::endl;
  }

  if (handModule->mousePressed(vsg::BUTTON_MASK_1) && state.getDrawingMode() == state::DrawingModeType::None &&
      state.getEditingMode() == state::EditingModeType::None && move_param.pressed_inter_move &&
      (state.getSelectMode() != state::SelectModeType::Part || !haveSelectPartObj(query))) {
    if (!move_param.enter_to_move) {
      auto& select_cmd = query.get_resource<select::SelectCmd>();
      if (select_cmd.hover_is_select()) {
        move_param.enter_to_move = true;
      }

      // bool haveSelectObj = false;
      // auto view = reg.view<select::SelectTag, select::HoverTag>();

      // bool inBox = false;
      // qlib::Vec2d mouse_pos = {axisModule.real_x, axisModule.real_y};

      // view.each([&haveSelectObj, &reg, &mouse_pos, &inBox](entt::entity e) {
      //   auto box = utils::getBox(reg, e);
      //   box = boxOver(box, 1.2, 0.0);

      //   if (qlib::point_in_box(box, mouse_pos, 0.001)) {
      //     inBox = true;
      //   }

      //   haveSelectObj = true;
      //   return;
      // });

      // if (haveSelectObj && inBox) {
      //   move_param.enter_to_move = true;
      //   // std::cout << "Enabel Enter Move ---------------" << std::endl;
      // }
    }

  } else {
    move_param.pressed_inter_move = true;
  }

  // std::cout << "Move info:" << move_param.pressed_inter_move << " " << move_param.enter_to_move << " " << handModule->mouse_move << std::endl;
  int dx, dy;
  if (state.getDrawingMode() == state::DrawingModeType::None && state.getEditingMode() == state::EditingModeType::None &&
      move_param.pressed_inter_move && move_param.enter_to_move && handModule->mouseMoved(&dx, &dy)) {
    // std::cout << "Enter Move ---------------" << std::endl;

    state.switchMode(state::EditingModeType::Move);
    move_param.wait_clock = false;
    move_param.enter_to_move = false;
    move_param.tuodong_first = true;
  }

  if (state.getEditingMode() != state::EditingModeType::Move) {
    return;
  }

  // 部分选择移动不生效
  if (state.getSelectMode() == state::SelectModeType::Part && haveSelectPartObj(query)) {
    state.exit();
    move_param.reset();
    return;
  }

  auto& select_models = query.get_resource<select::OverallModel>();
  // select_models.mouse_press = false;
  // select_models.mouse_release = false;

  // 获取cell和reg
  auto& layout_cmd = query.get_resource<layout::LayoutCmd>();

  // 获取输入 坐标系 撤销恢复
  auto& undoRedoModule = query.get_resource<undoRedoT::UndoRedoModule>();

  // 获取移动参数
  auto event_writer = query.get_event_writer<Event>();

  stateTransformBegin(move_param, state, handModule, bus, query);

  if (move_param.state == State::Cancel) {
    exit(query);
  }

  if (move_param.wait_clock && !move_param.first_clock) {
    qlib::Vec2d translate;
    bool exe = false;

    if (getKeyPressed("view", "Left shift of the drawing board", bus)) {
      translate = qlib::Vec2d(-1, 0);
      exe = true;
    }
    if (getKeyPressed("view", "Sketchpad Move Right", bus)) {
      translate = qlib::Vec2d(1, 0);
      exe = true;
    }
    if (getKeyPressed("view", "Move the drawing board up", bus)) {
      translate = qlib::Vec2d(0, 1);
      exe = true;
    }
    if (getKeyPressed("view", "Draw board down", bus)) {
      translate = qlib::Vec2d(0, -1);
      exe = true;
    }

    if (exe) {
      {
        auto& axis = query.get_resource<axis::Axis>();
        auto step = axis.grid.real_width / 20.0;
        translate *= step;
      }

      undoRedoModule.start(cell);

      auto view_old = reg.view<select::SelectTag>();
      view_old.each([&reg, &undoRedoModule, &translate, &layout_cmd](entt::entity entity) {
        // 备份改变前数据
        undoRedoModule.change(entity);

        // 移动旧对象
        utils::translate(reg, entity, translate);

        layout_cmd.update(entity);
      });

      // 执行undoRedo，被change的实体将绑定ObjChangeTag
      undoRedoModule.end();

      auto& sele_event_writer = query.get_event_writer<select::Event>();
      sele_event_writer.send({select::Code::SelectUpdate});
      sele_event_writer.send({select::Code::HoverUpdate});
    }
  }

  // 开始移动逻辑
  if (move_param.state == State::Upatde) {
    // 没有移动对象
    if (move_param.moveing_first) {
      move_param.moveing_first = false;
      move_param.moveing = true;

      // 遍历选中对象
      auto view = reg.view<select::SelectTag>();
      bool first = true;

      view.each([&reg, &layout_cmd, &event_writer, &first](entt::entity entity) {
        add_cache_obj(reg, event_writer, layout_cmd, entity, first);
        first = false;
      });

      // 记录第一次移动的鼠标位置，记录上一次鼠标位置
      move_param.first_pos = {axisModule.real_x, axisModule.real_y};
      move_param.last_pos = {axisModule.real_x, axisModule.real_y};

      update_cache_obj(reg, event_writer, layout_cmd, qlib::Vec2d(0, 0), qlib::Vec2d(0, 0));

    } else {
      // 有更新对象，计算距离上一次鼠标的偏移
      qlib::Vec2d now_pos = {axisModule.real_x, axisModule.real_y};
      now_pos = getNowPoint(query, move_param.first_pos);

      qlib::Vec2d translate_a = now_pos - move_param.last_pos;
      qlib::Vec2d translate_b = now_pos - move_param.first_pos;

      update_cache_obj(reg, event_writer, layout_cmd, translate_a, translate_b);
      // std::cout << "translate A: " << translate_a.x << " " << translate_a.y << std::endl;
      // std::cout << "translate B: " << translate_b.x << " " << translate_b.y << std::endl << std::endl;

      // 更新鼠标上一次位置
      move_param.last_pos = now_pos;
    }
  }

  // 完成流程
  if (move_param.state == State::Complete) {
    if (move_param.use_param) {
      if (!move_param.move_to_pos) {
        undoRedoModule.start(cell);

        qlib::Vec2d translate = move_param.move_vec;

        // 遍历旧对象
        auto view_old = reg.view<select::SelectTag>();
        view_old.each([&reg, &undoRedoModule, &translate, &layout_cmd](entt::entity entity) {
          // 移除操作标记
          reg.remove<MoveingTag>(entity);

          // 备份改变前数据
          undoRedoModule.change(entity);

          // 移动旧对象
          utils::translate(reg, entity, translate);

          layout_cmd.update(entity);
        });

        // 执行undoRedo，被change的实体将绑定ObjChangeTag
        undoRedoModule.end();

        if (move_param.moveing)
          delete_cache_obj(reg, event_writer, layout_cmd, query);

        // 移动执行完毕，回退到之前的模式
        state.exit();

        move_param.moveing = false;
        move_param.reset();

        auto& absorbModule = query.get_resource<qbe::adsorb::AdsorbModel>();
        absorbModule.close_diagonal_start();
      } else {
        undoRedoModule.start(cell);

        auto box = utils::getInitBox();
        auto view_old_t = reg.view<select::SelectTag>();
        view_old_t.each([&reg, &box](entt::entity entity) {
          auto box_t = utils::getBox(reg, entity);
          box = utils::boxAndBox(box, box_t);
        });

        qlib::Vec2d center = (box[0] + box[1]) / 2.0;

        // 到格点上
        center.x = int(center.x * 1000) / 1000.0;
        center.y = int(center.y * 1000) / 1000.0;

        qlib::Vec2d translate;
        if (!move_param.first_clock) {
          translate = move_param.move_vec - center;
        } else {
          translate = move_param.move_vec - move_param.pos_clock;
        }

        // 遍历旧对象
        auto view_old = reg.view<select::SelectTag>();
        view_old.each([&reg, &undoRedoModule, &translate, &layout_cmd](entt::entity entity) {
          //移除操作标记
          reg.remove<MoveingTag>(entity);

          // 备份改变前数据
          undoRedoModule.change(entity);

          // auto box = utils::getBox(reg, entity);
          // auto center = (box[0] + box[1]) / 2.0;

          // qlib::Vec2d vec = translate - center;

          // 移动旧对象
          utils::translate(reg, entity, translate);

          layout_cmd.update(entity);
        });

        // 执行undoRedo，被change的实体将绑定ObjChangeTag
        undoRedoModule.end();

        if (move_param.moveing)
          delete_cache_obj(reg, event_writer, layout_cmd, query);

        // 移动执行完毕，回退到之前的模式
        state.exit();

        move_param.moveing = false;
        move_param.reset();

        auto& absorbModule = query.get_resource<qbe::adsorb::AdsorbModel>();
        absorbModule.close_diagonal_start();
      }
    } else {
      // 计算鼠标距离第一次移动时的偏移
      qlib::Vec2d now_pos = {axisModule.real_x, axisModule.real_y};
      now_pos = getNowPoint(query, move_param.first_pos);

      qlib::Vec2d translate = now_pos - move_param.first_pos;

      std::vector<entt::entity> es;

      // 开始undoRedo命令记录
      undoRedoModule.start(cell);

      auto view_old = reg.view<select::SelectTag>();

      auto start = std::chrono::high_resolution_clock::now();

      view_old.each([&reg, &undoRedoModule, &translate, &layout_cmd, &es](entt::entity entity) {
        es.push_back(entity);

        // 移除操作标记
        reg.remove<MoveingTag>(entity);

        // 备份改变前数据
        undoRedoModule.change(entity);

        // 移动旧对象
        utils::translate(reg, entity, translate);
      });

      auto end = std::chrono::high_resolution_clock::now();

      // 计算持续时间
      auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);

      // 打印执行时间（秒）
      // std::cout << "Move translate time:" << duration.count() << " milliseconds" << std::endl;

      start = std::chrono::high_resolution_clock::now();

      // undoRedoModule.change(es);

      // std::cout << "UndoRedo update end" << std::endl;

      layout_cmd.update(es);

      // std::cout << "CMD update end" << std::endl;

      // 执行undoRedo，被change的实体将绑定ObjChangeTag
      undoRedoModule.end();

      end = std::chrono::high_resolution_clock::now();

      // 计算持续时间
      duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);

      // 打印执行时间（秒）
      // std::cout << "Move update time:" << duration.count() << " milliseconds" << std::endl;

      delete_cache_obj(reg, event_writer, layout_cmd, query);

      // 移动执行完毕，回退到之前的模式
      state.exit();

      move_param.moveing = false;
      move_param.reset();

      auto& absorbModule = query.get_resource<qbe::adsorb::AdsorbModel>();
      absorbModule.close_diagonal_start();
    }
  }

  stateTransformEnd(move_param.state);
}

void Plugin::build(ecs::App& app) {
  app.add_stage<InitStage>();
  app.add_stage<UpdateStage>();

  app.init_event<Event>();

  app.add_systems<InitStage>(init);
  app.add_systems<UpdateStage>(update);
  app.add_systems<ecs::stage::Last>(clear_tag);
  app.add_systems<ecs::stage::Last>(clear_events);
}

}  // namespace qbe::move